package elephant.servers.consoleserver.command;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import elephant.Container;
import elephant.servers.consoleserver.AnsiColor;
import elephant.servers.consoleserver.termos.ReadLineEnvironment;

/**
 * 
 * @author icecooly
 *
 */
public abstract class SshdCommand{
	//
	private static Logger logger=LoggerFactory.getLogger(SshdCommand.class);
	//
	public String name;
	public String desc;
	Options options;
	Map<String,Consumer<String>> callbackMap;
	//
	InputStream stdin;
	OutputStream stdout;
	OutputStream stderr;
	ReadLineEnvironment environment;
	ScheduledFuture<?> futrue;
	String command;
	String args;
	boolean isLoop;
	//
	public SshdCommand(String name,String desc){
		this.name=name;
		this.desc=desc;
		this.options=new Options();
		this.callbackMap=new HashMap<>();
		this.addOption("h",false,"show help info.",this::printHelp);
	}

	public final void run(InputStream stdin, OutputStream stdout, OutputStream stderr,
    		ReadLineEnvironment environment, String[] args) throws Exception{
		this.stdin=stdin;
		this.stdout=stdout;
		this.stderr=stderr;
		this.environment=environment;
		//如果有定时器则删除
		if(futrue!=null){
			futrue.cancel(true);
		}
		//
		if(args.length==0){
			printHelp("");
			return;
		}
		excute(args);
	}
	
	private void excute(String[] args) throws IOException {
		//
		this.command=args[0];
		this.args=getOptionValue(args);
		if(!callbackMap.containsKey(command)){
			printUnknownCommand(command);
			return;
		}
		excute();
		//
		//if -loop
		if(isLoop){
			futrue=Container.get().scheduleAtFixedRate(this::onLoopExcute,0,1,TimeUnit.SECONDS);
		}
	}
	
	private void excute(){
		callbackMap.get(command).accept(args);
	}
	
	private void onLoopExcute(){
		print("\033c");
		printlnHomeString();
		excute();
	}
	
	public void onControlC(){
		System.out.println("onControlC futrue:"+futrue);
		if(futrue!=null){
			futrue.cancel(true);
		}
	}
	
	public void afterExcute(String command) throws IOException{
		printlnHomeString();
	}

	protected void addOption(String opt,boolean hasArg, String description,Consumer<String> callback){
		options.addOption(opt, hasArg, description);
		callbackMap.put("-"+opt, callback);
	}
	
	public void printHelp(String opt){
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		HelpFormatter formatter = new HelpFormatter();
		formatter.printHelp(pw, 80, name, desc, options, 3, 3, "", true);
		pw.flush();
		pw.close();
		println(sw.toString());
	}
	
	protected boolean hasOption(String command,String b){
		if(command==null||b==null){
			return false;
		}
		String[] cmds=command.split(" ");
		if(cmds.length<1){
			return false;
		}
		return (cmds[0].trim()).equals("-"+b);
	}
	
	private String getOptionValue(String[] args){
		isLoop=false;
		if(args==null){
			return "";
		}
		if(args.length<=1){
			return "";
		}
		StringBuilder sb=new StringBuilder();
		for(int i=1;i<args.length;i++){
			if(i==args.length-1){
				if(args[i].equals("-loop")){//
					isLoop=true;
					continue;
				}
			}
			sb.append(args[i]).append(" ");
		}
		return sb.toString().trim();
	}
	//
	protected void printUnknownCommand(String command) throws IOException {
		println(AnsiColor.getYellowContent("Unknown command '"+command+"'"));
	}
	//
	protected void printlnHomeString(){
		println(environment.prompt);
	}
	//
	protected void printHomeString() throws IOException {
		print(environment.prompt);
	}
	//
	protected void print(String content){
		if(content==null){
			return;
		}
		if(stdout==null){
			return;
		}
		try {
			stdout.write((content).getBytes());
			stdout.flush();
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
	}
	//
	protected void formatPrintln(String format,Object ... args){
		println(String.format(format, args));
	}
	//
	protected void println(String content){
		if(content==null){
			return;
		}
		if(stdout==null){
			return;
		}
		try {
			stdout.write((content+"\r\n").getBytes());
			stdout.flush();
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
		
	}
}
